﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Net;
using System.Text;
using System.Windows.Forms;
using Newtonsoft.Json;

namespace PoolSide
{
    public partial class frmMain : Form
    {
        #region Variables
        private List<WorkerInfo> Workers;
        private List<BlockInfo> Blocks;
        private List<ExchangeInfo> Exchanges;
        private Timer timer;
        #endregion

        #region Form events
        public frmMain()
        {
            InitializeComponent();
        }
        private void frmMain_Load(object sender, EventArgs e)
        {
            
            var dummy = Program.CurrencyExchanges;
        }
        private void frmMain_Shown(object sender, EventArgs e)
        {
            this.Cursor = Cursors.WaitCursor;
            Application.DoEvents();
            try
            {
                if (string.IsNullOrEmpty(Program.ApiKey))
                {
                    cmdRefreshAccountTab.Enabled = false;
                    chkIsAutoRefresh.Enabled = false;
                    nudRefreshInterval.Enabled = false;
                    cmdPoolInfo.Enabled = false;
                    this.Cursor = Cursors.Default;
                    return;
                }

                chkIsAutoRefresh.Checked = Program.IsAutoRefresh;
                nudRefreshInterval.Value = Program.RefreshInterval;
                if (!string.IsNullOrEmpty(Program.ApiKey)) cmdRefreshAccountTab.Enabled = true;
                RefreshWorkerTab();
                RefreshRoundsTab();
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                this.Cursor = Cursors.Default;
            }
        }
        #endregion

        #region Timer event
        private void timer_Tick(object sender, EventArgs e)
        {
            timer.Stop();
            RefreshWorkerTab();
            RefreshRoundsTab();
            timer.Start();
        }
        #endregion

        #region Non-control void methods
        private void RefreshWorkerTab()
        {
            this.Cursor = Cursors.WaitCursor;
            try
            {
                if (string.IsNullOrEmpty(Program.ApiKey))
                    return;

                string jsonAcct = Program.GetUserAccountJson();
                StringReader sr = new StringReader(jsonAcct);
                JsonTextReader jtr = new JsonTextReader(sr);

                while (jtr.Read())
                {
                    if (jtr.TokenType == JsonToken.PropertyName)
                    {
                        switch (jtr.Value.ToString().ToUpper())
                        {
                            case "USERNAME": Program.Username = jtr.ReadAsString(); break;
                            case "SEND_THRESHOLD": Program.SendThreshold = jtr.ReadAsDecimal().Value.RemoveEndZeroes(); break;
                            case "CONFIRMED_REWARD": Program.ConfirmedReward = jtr.ReadAsDecimal().Value; break;
                            case "WALLET": Program.Wallet = jtr.ReadAsString(); break;
                            case "UNCONFIRMED_REWARD": Program.UnconfirmedReward = jtr.ReadAsDecimal().Value; break;
                            case "HASHRATE": Program.Hashrate = jtr.ReadAsDecimal().Value; break;
                            case "WORKERS":
                                int startPosition = jtr.LinePosition + 2;
                                int endPosition = jsonAcct.IndexOf("}}");
                                string workString = jsonAcct.Substring(startPosition, (endPosition - startPosition) + 1);
                                while (workString.IndexOf(' ') > 0) workString = workString.Replace(" ", "");
                                LoadWorkers(workString);
                                break;
                        }
                    }
                }

                ShowData();
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                this.Cursor = Cursors.Default;
            }
        }
        private void RefreshRoundsTab()
        {
            this.Cursor = Cursors.WaitCursor;
            try
            {
                if (string.IsNullOrEmpty(Program.ApiKey))
                    return;
                string jsonRound = Program.GetPoolRoundJson();
                StringReader sr = new StringReader(jsonRound);
                JsonTextReader jtr = new JsonTextReader(sr);

                while (jtr.Read())
                {
                    if (jtr.TokenType == JsonToken.PropertyName)
                    {
                        try
                        {
                            switch (jtr.Value.ToString().ToUpper())
                            {
                                case "ROUND_DURATION": Program.RoundDuration = jtr.ReadAsString(); break;
                                case "GHASHES_PS": Program.PoolPerformance = jtr.ReadAsString(); break;
                                case "SHARES": Program.CurrentRoundShares = jtr.ReadAsString(); break;
                                case "SCORE": Program.CurrentRoundScore = jtr.ReadAsString(); break;
                                case "ROUND_STARTED": Program.CurrentRoundStarted = jtr.ReadAsDateTime().Value; break;
                                case "LUCK_7": Program.Luck7 = jtr.ReadAsString(); break;
                                case "LUCK_30": Program.Luck30 = jtr.ReadAsString(); break;
                                case "LUCK_1": Program.Luck1 = jtr.ReadAsString(); break;
                                case "SHARES_CDF": Program.CurrentSharesCDF = jtr.ReadAsString(); break;
                                case "BLOCKS":
                                    int startPosition = jtr.LinePosition + 2;
                                    int endPosition = jsonRound.IndexOf("}}");
                                    string workString = jsonRound.Substring(startPosition, (endPosition - startPosition) + 1);
                                    while (workString.IndexOf(' ') > 0) workString = workString.Replace(" ", "");
                                    LoadBlocks(workString);
                                    Blocks.ForEach(obj => lvwRound.Items.Add(obj.GetListViewItem()));
                                    break;
                            }
                        }
                        catch (Exception)
                        {
                            throw;
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                this.Cursor = Cursors.Default;
            }
        }
        private void ShowData()
        {
            lblUnconfirmedReward.Text = Program.UnconfirmedReward.ToString();
            lblConfirmedReward.Text = Program.ConfirmedReward.ToString();
            lblThreshold.Text = Program.SendThreshold.ToString();
            statusWallet.Text = Program.Wallet;
            lblAvgRate.Text = Program.Hashrate.ToString();
            lblTotal.Text = (Program.ConfirmedReward + Program.UnconfirmedReward).ToString();
            statusLastUpdated.Text = string.Concat("Last updated:  ", DateTime.Now.ToString("ddd MM/dd/yyyy hh:mm tt"));

            lvwPool.Items.Clear();
            if (Workers == null) return;

            foreach (WorkerInfo wi in Workers)
            {
                lvwPool.Items.Add(wi.ToListViewItem());
            }
        }
        private void LoadWorkers(string input)
        {
            Workers = new List<WorkerInfo>();

            string[] sep = new string[1];
            sep[0] = "},";
            string[] rawWorker = input.Split(sep, StringSplitOptions.None);

            WorkerInfo wi = null;

            foreach (string str in rawWorker)
            {
                wi = new WorkerInfo();
                wi.Name = str.Substring(1, str.IndexOf(':') - 2);

                string json = str.Substring(str.IndexOf('{'));
                if (!json.EndsWith("}")) json += "}";

                StringReader sr = new StringReader(json);
                JsonTextReader tr = new JsonTextReader(sr);

                while (tr.Read())
                {
                    if (tr.TokenType == JsonToken.PropertyName)
                    {
                        switch (tr.Value.ToString().ToUpper())
                        {
                            case "SCORE": wi.Score = tr.ReadAsDecimal().Value; break;
                            case "HASHRATE": wi.Hashrate = tr.ReadAsDecimal().Value; break;
                            case "SHARES": wi.Shares = tr.ReadAsInt32().Value; break;
                            case "ALIVE": wi.Alive = Convert.ToBoolean(tr.ReadAsString()); break;
                        }
                    }
                }

                Workers.Add(wi);
            }
        }
        private void LoadBlocks(string input)
        {
            Blocks = new List<BlockInfo>();

            char[] delim = new char[1];
            delim[0] = '}';

            string[] parts = input.Split(delim);
            string workString = "";

            foreach (string part in parts)
            {
                if (!string.IsNullOrEmpty(part))
                {
                    workString = part;
                    if (workString.Substring(0, 1) == ",")
                        workString = workString.Substring(1);

                    if (!workString.EndsWith("}"))
                        workString += "}";

                    Blocks.Add(new BlockInfo(workString));
                }
            }
        }
        private void FilterExchangeListByCurrency(string currency)
        {
            lvwExchange.Items.Clear();

            List<ExchangeInfo> filteredList = Exchanges.FindAll(obj => obj.Currency.Trim().ToUpper() == currency);

            if (chkHideUnavailable.Checked)
                filteredList.RemoveAll(exc => exc.IsUnavailable == false);

            if (optConfirmed.Checked)
                filteredList.ForEach(o2 => lvwExchange.Items.Add(o2.GetListViewItem("confirmed")));
            else
                filteredList.ForEach(o2 => lvwExchange.Items.Add(o2.GetListViewItem("total")));

            decimal minAsk = 99999M;
            decimal maxBid = 0M;
            string minAskSymbol = "";
            string maxBidSymbol = "";

            filteredList.ForEach(obj => 
                {
                    if (obj.Ask < minAsk)
                    {
                        minAsk = obj.Ask.Value;
                        minAskSymbol = obj.Symbol;
                    }
                } 
            );
            filteredList.ForEach(obj => 
                {
                    if (obj.Bid > maxBid)
                    {
                        maxBid = obj.Bid.Value;
                        maxBidSymbol = obj.Symbol;
                    }
                } 
            );

            lblBid.Text = string.Concat(
                string.Format("{0:C}", maxBid),
                " (", maxBidSymbol, ")");
            lblAsk.Text = string.Concat(
                string.Format("{0:C}", minAsk),
                " (", minAskSymbol, ")");

            lblRange.Text = string.Format("{0:C}", maxBid - minAsk);


            // STUB - work-in-progress
            cmdInteraction.Enabled = false;
            cmdWebsite.Enabled = false;
            // END STUB
        }
        #endregion

        #region Index and check-changed events
        private void optTotal_CheckedChanged(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(ddlCurrency.Text))
                FilterExchangeListByCurrency(ddlCurrency.Text);
        }
        private void optConfirmed_CheckedChanged(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(ddlCurrency.Text))
                FilterExchangeListByCurrency(ddlCurrency.Text);
        }
        private void chkHideUnavailable_CheckedChanged(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(ddlCurrency.Text))
                FilterExchangeListByCurrency(ddlCurrency.Text);
        }
        private void ddlCurrency_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(ddlCurrency.Text))
            {
                Properties.Settings.Default["PrefCurrency"] = ddlCurrency.Text;
                Properties.Settings.Default.Save();
                FilterExchangeListByCurrency(ddlCurrency.Text);
            }
        }
        private void tabMain_SelectedIndexChanged(object sender, EventArgs e)
        {
            switch (tabMain.SelectedIndex)
            {
                case 0: // Account information
                    // more here?
                    break;
                case 1:  // Pool rounds
                    // more here?
                    break;
                case 2:  // Market data
                    this.Cursor = Cursors.WaitCursor;
                    try
                    {
                        Application.DoEvents();
                        string json = Program.GetMarketsAPIJson();
                        
                        Exchanges = new List<ExchangeInfo>();
                        Exchanges = JsonConvert.DeserializeObject<List<ExchangeInfo>>(json);

                        string prefCurrency = Properties.Settings.Default["PrefCurrency"].ToString();

                        if (ddlCurrency.Items.Count == 0)
                        {
                            Program.Currencies.ForEach(obj => ddlCurrency.Items.Add(obj));

                            if (!string.IsNullOrEmpty(prefCurrency))
                            {
                                int index = ddlCurrency.FindString(prefCurrency);
                                if (ddlCurrency.FindString(prefCurrency) > -1)
                                    ddlCurrency.SelectedIndex = index;
                            }
                        }

                        // if we have selected a currency  and we have a preferred currency then filter the list
                        if (!string.IsNullOrEmpty(ddlCurrency.Text) &&
                            !string.IsNullOrEmpty(prefCurrency))
                        {
                            FilterExchangeListByCurrency(prefCurrency);
                        }



                    }
                    catch (WebException)
                    {
                        MessageBox.Show("Unable to contact BitcoinCharts Market data.  Please try again later.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    catch (Exception)
                    {
                        throw;
                    }
                    finally
                    {
                        this.Cursor = Cursors.Default;
                    }
                    break;
                case 3:  // About tab

                    break;
            }
        }
        private void chkIsAutoRefresh_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default["IsAutoRefresh"] = chkIsAutoRefresh.Checked;
            Properties.Settings.Default.Save();
            if (chkIsAutoRefresh.Checked)
            {
                if (timer == null)
                {
                    timer = new Timer();
                    timer.Interval = Convert.ToInt32((nudRefreshInterval.Value * 1000M) * 60);
                    timer.Tick += new EventHandler(timer_Tick);
                }
                timer.Start();
            }
            else
                if (timer != null) timer.Stop();
        }
        private void nudRefreshInterval_ValueChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default["RefreshInterval"] = nudRefreshInterval.Value.ToString();
            Properties.Settings.Default.Save();
            if (timer.Enabled)
            {
                timer.Stop();
                timer.Start();
            }
        }
        private void lvwExchange_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lvwExchange.SelectedItems.Count > 0)
            {
                cmdWebsite.Enabled = true;
                ExchangeInfo ei = lvwExchange.SelectedItems[0].Tag as ExchangeInfo;
                
                // If we have market/API access enable the interaction button.
                // if not, disable:
                cmdInteraction.Enabled = ei.IsApi;
            }
        }
        #endregion

        #region Button and link click events
        private void cmdSetApiKey_Click(object sender, EventArgs e)
        {
            frmAPIKey formKey = new frmAPIKey();
            DialogResult dr = formKey.ShowDialog();
            if (dr == DialogResult.Cancel)
                return;

            if (!string.IsNullOrEmpty(Program.ApiKey))
            {
                cmdRefreshAccountTab.Enabled = true;
                RefreshWorkerTab();
                RefreshRoundsTab();
                nudRefreshInterval.Enabled = true;
                chkIsAutoRefresh.Enabled = true;
            }
        }
        private void cmdRefreshAccountTab_Click(object sender, EventArgs e)
        {
            RefreshWorkerTab();
            RefreshRoundsTab();
        }
        private void cmdWebsite_Click(object sender, EventArgs e)
        {
            ExchangeInfo ei = lvwExchange.SelectedItems[0].Tag as ExchangeInfo;
        }
        private void cmdInteraction_Click(object sender, EventArgs e)
        {
            ExchangeInfo ei = lvwExchange.SelectedItems[0].Tag as ExchangeInfo;
            frmMarket formMarket = new frmMarket(ei);
            formMarket.ShowDialog();
        }
        private void cmdCopy_Click(object sender, EventArgs e)
        {
            Clipboard.SetText(txtDevWallet.Text);
        }
        private void cmdPoolInfo_Click(object sender, EventArgs e)
        {
            frmPoolInfo form = new frmPoolInfo();
            form.ShowDialog();
        }
        private void llBitcoinChartsWebsite_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start(@"http://www.bitcoincharts.com");
        }
        #endregion
    }
}